#include "HttpMgrSubsystem.h"
#include "AsyncDownloadFile.h"

UHttpManagerSetting::UHttpManagerSetting()
	:MaxParallel(5),
	MaxTryCount(10),
	RequestKBSize(1024),
	ResponseTimeout(5.0f)
{
	CurFilePath = FPaths::ProjectSavedDir() / TEXT("HttpCacheFiles");
}

void UHttpMgrSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
	Super::Initialize(Collection);

	Init();
}

void UHttpMgrSubsystem::Deinitialize()
{
	Super::Deinitialize();

	ClearMgr();
}

void UHttpMgrSubsystem::Tick(float DeltaTime)
{
	if (IsTickable())
	{
		for (int32 i = 0; i < DownloadHttpFiles.Num(); ++i)
		{
			UAsyncDownloadFile* HttpFile = DownloadHttpFiles[i];
			if (HttpFile)
			{
				if (HttpFile->State == ETaskState::_Downloading)
				{
					double CurTime = FApp::GetCurrentTime();
					HttpFile->CheckDeadTask(CurTime);
				}
			}
			else
			{
				DownloadHttpFiles.RemoveAt(i);
			}
		}
	}
}

bool UHttpMgrSubsystem::IsTickable() const
{
	return DownloadHttpFiles.Num() > 0;
}

UAsyncDownloadFile* UHttpMgrSubsystem::CreateDownTask(UObject* WorldContextObject, const FString& InUrl, const FString& InFileName, const FString& InDirectory, bool bClearCache)
{
	UAsyncDownloadFile* HttpFile = NewObject<UAsyncDownloadFile>(WorldContextObject, *(InDirectory / InFileName));
	if (HttpFile)
	{
		HttpFile->Url = InUrl;
		HttpFile->FileName = InFileName;
		HttpFile->Directory = InDirectory;
		HttpFile->State = ETaskState::_Ready;
		HttpFile->Mgr = this;
		HttpFile->bClearCache = bClearCache;
		HttpFile->MD5Str = FMD5::HashAnsiString(*InUrl);
		DownloadHttpFiles.AddUnique(HttpFile);
	}

	return HttpFile;
}

void UHttpMgrSubsystem::ExecDownloadTasks()
{
	int32 NumActiveTask = 0;
	TArray<UAsyncDownloadFile*> ProbTasks;
	for (int32 i = 0; i < DownloadHttpFiles.Num(); ++i)
	{
		UAsyncDownloadFile* HttpFile = DownloadHttpFiles[i];
		if (HttpFile)
		{
			ETaskState State = HttpFile->State;
			switch (State)
			{
				case ETaskState::_Ready:
					ProbTasks.Add(HttpFile);
					break;
				case ETaskState::_Downloading:
					NumActiveTask++;
					break;
				case ETaskState::_Success:
					DownloadHttpFiles.Remove(HttpFile);
					break;
				case ETaskState::_Retry:
					if (HttpFile->TryCount <= MaxTryCount)
					{
						ProbTasks.Add(HttpFile);
					}
					break;
				case ETaskState::_Failed:
					break;
			}
		}
	}

	for (int32 i = 0; i < ProbTasks.Num(); ++i)
	{
		UAsyncDownloadFile* HttpFile = ProbTasks[i];
		if (HttpFile && NumActiveTask < MaxParallel)
		{
			HttpFile->StartDownloadTask();
		}
	}
}

void UHttpMgrSubsystem::NotifyFailed(UAsyncDownloadFile* HttpFile, bool bForceFailed)
{
	if (!bForceFailed)
	{
		if (HttpFile)
		{
			if (HttpFile->TryCount < MaxTryCount)
			{
				HttpFile->NotifyWait();
			}
			else
			{
				HttpFile->NotifyFailed();
			}
		}
	}
	ExecDownloadTasks();
}

void UHttpMgrSubsystem::NotifyFinished(UAsyncDownloadFile* HttpFile)
{
	ExecDownloadTasks();
	ClearMgr();
}

void UHttpMgrSubsystem::ClearMgr()
{
	for (int32 i = DownloadHttpFiles.Num() - 1; i >= 0; --i)
	{
		DownloadHttpFiles.RemoveAt(i);
	}
}

void UHttpMgrSubsystem::Init()
{
	const UHttpManagerSetting* Config = GetDefault<UHttpManagerSetting>();
	this->MaxParallel = Config->MaxParallel;
	this->MaxTryCount = Config->MaxTryCount;
	this->RequestKBSize = Config->RequestKBSize;
	this->ResponseTimeout = Config->ResponseTimeout;
	this->CurFilePath = Config->CurFilePath;
}
